package com.huanf.controller;

import com.huanf.domain.ExcelToMySQLVo;
import com.huanf.service.ExcelToMySQLService;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: 35238
 * 功能: 批量读取Excel的数据，并插入到数据库
 * 时间: 2023-12-23 21:03
 */
@RestController
public class ExcelToMySQLController {

    @Autowired
    private ExcelToMySQLService excelToMySQLService;

    @GetMapping("/") //浏览器访问 http://localhost:8080/，然后你mysql的java_ready库的excel_to_mysql表就会多50条数据
    public void demo() throws Exception{

        //表格其它行，也就是除第一行的其它行，要把这些数据插入数据库
        ArrayList<ExcelToMySQLVo> vo = new ArrayList<>();

        //表格第一行，也就是每一列的列头
        List<String> yypropertyNames = new ArrayList<>();

        //表格的位置。为了方便写的是项目路径
        String PATH = "E:\\桌面\\Java-Ready\\conf\\Excel\\";
        //获取表格文件
        File file = new File(PATH + "ReadExcelToMySQL.xlsx");
        //读取表格文件
        FileInputStream fileInputStream = new FileInputStream(file);

        //创建工作簿
        Workbook workbook = WorkbookFactory.create(fileInputStream);
        //获取有多少sheet，我们测试表格里面只有一个sheet
        int numberOfSheets = workbook.getNumberOfSheets();
        for (int i=0; i<numberOfSheets; i++) {
            //获取其中一个sheet
            Sheet sheet = workbook.getSheetAt(i);
            //获取这个sheet有多少行
            int physicalNumberOfRows = sheet.getPhysicalNumberOfRows();
            for (int j=0; j<physicalNumberOfRows; j++) {
                //获取这个sheet的每一行
                Row row = sheet.getRow(j);
                //获取第一行有多少个单元格
                int physicalNumberOfCells = row.getPhysicalNumberOfCells();
                //如果是第一行，第一行比较特殊，是每一列的列头
                if (j==0) {
                    //获取第一行的每个单元格
                    for (int k = 0; k < physicalNumberOfCells; k++) {
                        // 获取到的cell就是第一行的每一个格子
                        Cell cell = row.getCell(k);

                        if (cell != null) {
                            //getCellType判断每一行的每一个格子的数据类型。类型被poi官方封装成了枚举类，里面用数字表示类型。可以点击getCellType看看源码
                            int cellType = cell.getCellType();
                            String cellValue = ""; //格子里面的具体数据
                            switch (cellType) {
                                case XSSFCell.CELL_TYPE_STRING://如果是字符串
                                    cellValue = cell.getStringCellValue();//getStringCellValue方法只能获取字符串数据
                                    break;
                                case XSSFCell.CELL_TYPE_BOOLEAN://如果是布尔值
                                    cellValue = String.valueOf(cell.getBooleanCellValue());//getBooleanCellValue方法只能获取布尔值数据
                                    break;
                                case XSSFCell.CELL_TYPE_NUMERIC://如果是数字(包括日期、普通数字)
                                    if (HSSFDateUtil.isCellDateFormatted(cell)){ //如果是日期
                                        cellValue = new DateTime(cell.getDateCellValue()).toString("yyyy-MM-dd");//getStringCellValue方法只能获取日期数据
                                    }else {
                                        cell.setCellType(XSSFCell.CELL_TYPE_STRING);//注意数字类型要转换成字符串类型，才能输出，避免数字过长
                                        cellValue = cell.toString();
                                    }
                                    break;
                                case XSSFCell.CELL_TYPE_ERROR://如果表格里的数据类型不对
                                    throw new RuntimeException("表格里的数据类型不对");
                            }
                            yypropertyNames.add(cellValue);
                            //输出这些格子的数据
                            //System.out.print(cellValue + " | ");
                        }
                    }
                    System.out.println();
                } else{//其它行，除第一行之外的其它行。这些数据是要插进数据库的
                    ExcelToMySQLVo excelToMySQLVo = new ExcelToMySQLVo();
                    for (int k = 0; k < physicalNumberOfCells; k++) {
                        // 获取到的cell就是第一行的每一个格子
                        Cell cell = row.getCell(k);
                        if (cell != null) {
                            //getCellType判断每一行的每一个格子的数据类型。类型被poi官方封装成了枚举类，里面用数字表示类型。可以点击getCellType看看源码
                            int cellType = cell.getCellType();
                            String cellValue = ""; //格子里面的具体数据
                            switch (cellType) {
                                case XSSFCell.CELL_TYPE_STRING://如果是字符串
                                    cellValue = cell.getStringCellValue();//getStringCellValue方法只能获取字符串数据
                                    break;
                                case XSSFCell.CELL_TYPE_BOOLEAN://如果是布尔值
                                    cellValue = String.valueOf(cell.getBooleanCellValue());//getBooleanCellValue方法只能获取布尔值数据
                                    break;
                                case XSSFCell.CELL_TYPE_NUMERIC://如果是数字(包括日期、普通数字)
                                    if (HSSFDateUtil.isCellDateFormatted(cell)){ //如果是日期
                                        cellValue = new DateTime(cell.getDateCellValue()).toString("yyyy-MM-dd");//getStringCellValue方法只能获取日期数据
                                    }else {
                                        cell.setCellType(XSSFCell.CELL_TYPE_STRING);//注意数字类型要转换成字符串类型，才能输出，避免数字过长
                                        cellValue = cell.toString();
                                    }
                                    break;
                                case XSSFCell.CELL_TYPE_ERROR://如果表格里的数据类型不对
                                    throw new RuntimeException("表格里的数据类型不对");
                            }
                            //输出这些格子的数据
                            //System.out.print(cellValue + " | ");

                            //调用下面写好的方法
                            excelToMySQLVo = MysetValue(excelToMySQLVo, yypropertyNames.get(k), cellValue);
                        }
                    }
                    vo.add(excelToMySQLVo);
                    System.out.println();
                }
            }
        }

        System.out.println("数据读取完成。集合大小:【" + vo.size() + "】条数据");
        excelToMySQLService.saveBatch(vo);//把集合里的数据保存到数据库
    }

    /**
     * 通过反射来执行实体类的set。该方法使用的前提是 你Excel表格每行的列头名字必须跟数据库里的字段一致(实体类的字段都用字符串，数据库的字段随便)
     * @param vo 实体类
     * @param xxpropertyName 实体类的字段
     * @param xxvalue 字段的值
     * @return
     */
    private static ExcelToMySQLVo MysetValue(ExcelToMySQLVo vo, String xxpropertyName, String xxvalue) throws Exception {

        //通过反射先获取到实体类的class对象
        Class<? extends ExcelToMySQLVo> clazz = vo.getClass();

        //通过反射获取实体类的set方法(通过getWriteMethod来获取)
        PropertyDescriptor descriptor = new PropertyDescriptor(xxpropertyName, clazz);
        Method getMethod = descriptor.getWriteMethod();
        getMethod.invoke(vo, xxvalue);

        return vo;
    }

}
